(function (root, factory) {
    if (typeof define === "function" && define.amd) define([], factory);
    else if (typeof module === "object" && module.exports) module.exports = factory();
    else root.Secure = factory();
}(typeof self !== "undefined" ? self : this, function () {
    "use strict";

    function Secure({secureServer, disguise}) {
        if (!(this instanceof Secure)) {
            throw new Error("Secure must be instantiated with new");
        }
        this.secureServer = secureServer;
        this.disguise = disguise;
    }

    //  =========== 内置工具 ==========
    const SMHelper = {
        mode: "cbc",
        sm2Encrypt: (data, key) => "04" + sm2.doEncrypt(data, key),
        sm2Decrypt: (data, key) => sm2.doDecrypt(data.substring(2), key),
        sm2KeyPair: () => sm2.generateKeyPairHex(),
        sm3Hash: (data) => sm3(data),
        sm4Encrypt: (data, key, iv) => sm4.encrypt(data, key, {mode: SMHelper.mode, iv}),
        sm4Decrypt: (data, key, iv) => sm4.decrypt(data, key, {mode: SMHelper.mode, iv})
    };
    const disguiseMap = {
        img: e => new Disguise(e).getKeyByImg(),
        font: e => new Disguise(e).getKeyByFont(),
        binary: e => new Disguise(e).getKeyByBinary()
    };

    function getText(url, body, headers) {
        return fetch(url, {method: "POST", body, headers}).then(e => e.text());
    }

    /**
     * FormData 转 JSON
     */
    FormData.prototype.toJSON = function () {
        const json = {};
        for (const key of this.keys()) {
            const value = this.getAll(key);
            if (value == null) continue;
            json[key] = value.length === 1 ? value[0] : value;
        }
        return json;
    };

    /**
     * JSON 转 FormData
     */
    Object.prototype.toFormData = function () {
        const form = new FormData();
        for (let key of Object.keys(this)) {
            if (this[key] == null) continue;
            form.append(key, this[key]);
        }
        return form;
    };

    // 设备指纹作为IV
    function getIv(salt) {
        salt && (SMHelper.hash = salt);
        const canvasIv = () => {
            const canvas = document.createElement("canvas");
            canvas.width = 200;
            canvas.height = 100;
            const ctx = canvas.getContext("2d");
            ctx.textBaseline = "top";
            ctx.font = "14px Arial";
            ctx.fillStyle = "#f60";
            ctx.fillRect(125, 1, 62, 20);
            ctx.fillStyle = "#069";
            ctx.fillText("Hello, world! 🌍", 2, 15);
            ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
            ctx.fillText("Hello, world! 🌍", 4, 17);
            return canvas.toDataURL();
        };
        const fingerprints = {
            canvas: canvasIv(),
            userAgent: navigator.userAgent,
            language: navigator.language,
            platform: navigator.platform,
            width: screen.width,
            height: screen.height,
            colorDepth: screen.colorDepth,
            timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
            cookiesEnabled: navigator.cookieEnabled,
            doNotTrack: navigator.doNotTrack,
            salt: salt || SMHelper.hash
        };
        // 生成设备指纹
        return SMHelper.sm3Hash(JSON.stringify(fingerprints)).substring(0, 32);
    }

    /**
     * 加密的核心方法
     * @param data 基本类型/JSON/FormData
     * @param encryptKeys 要加密的key
     * @returns {FormData|{}|null|string}
     */
    Secure.prototype.encrypt = function (data, encryptKeys) {
        // 处理 null/空字符串
        if (data == null) return data;
        if (typeof data === "string" && data === "") return data;
        // 基本类型直接加密
        if (["string", "number", "boolean"].includes(typeof data)) {
            return String(data).encrypt();
        }
        // 统一转为 JSON 对象处理
        const isFormData = data instanceof FormData;
        const rawData = isFormData ? data.toJSON() : data;
        // 生成签名（基于原始数据）
        const signature = this.sign(rawData);
        // 处理加密字段
        const ENC = {};
        for (const [key, value] of Object.entries(rawData)) {
            if (encryptKeys.includes(key)) {
                if (value == null) {
                    ENC[key] = null;
                } else if (["string", "number", "boolean"].includes(typeof value)) {
                    ENC[key] = String(value).encrypt(); // 基本类型
                } else if (Array.isArray(value) || typeof value === "object") {
                    ENC[key] = JSON.stringify(value).encrypt(); // 复杂类型序列化
                } else {
                    throw new Error(`不支持加密的类型: ${typeof value}`);
                }
            } else {
                ENC[key] = value; // 明文字段
            }
        }
        // 添加加密后的签名
        ENC["sign"] = signature;
        return isFormData ? ENC.toFormData() : ENC;
    };

    // 开始交换对称密钥
    Secure.prototype.exchange = function () {
        return fetch(this.secureServer, {method: "POST"})
            .then(async e => [await disguiseMap[this.disguise](await e.arrayBuffer()), e.headers])
            .then(async ([pk, apis]) => {
                const headers = {};
                const pair = SMHelper.sm2KeyPair();
                const body = SMHelper.sm2Encrypt(pair.publicKey, pk);
                headers["x-server-digest"] = SMHelper.sm3Hash(pk);
                headers["x-client-digest"] = SMHelper.sm3Hash(pair.publicKey);
                headers["x-browser-sign"] = getIv(JSON.stringify(headers));
                const enSm4 = await getText(apis.get("x-exchange-api"), body, headers);
                SMHelper.sk = SMHelper.sm2Decrypt(enSm4, pair.privateKey);
                SMHelper.pk = pair.privateKey;
                startRotationTimer(apis.get("x-rotation-api"));
            });
    };

    // 签名生成逻辑
    Secure.prototype.sign = function (json) {
        const _join = [];
        const _time = Date.now().toString(16);
        Object.values(json).forEach(obj => {
            if (obj == null) {
                return _join.push("null");
            }
            if (["string", "number", "boolean"].includes(typeof obj)) {
                return _join.push(String(obj));
            }
            _join.push(JSON.stringify(obj));
        });
        const digest = SMHelper.sm3Hash(_join.join("") + _time);
        return (digest + _time).encrypt(); // 返回加密后的签名
    };

    // 注册原型方法（通用加密解密）
    String.prototype.encrypt = function () {
        return "<" + SMHelper.sm4Encrypt(this.toString(), SMHelper.sk, getIv()) + ">";
    };
    String.prototype.decrypt = function () {
        return SMHelper.sm4Decrypt(this.toString(), SMHelper.sk, getIv());
    };
    String.prototype.deJSON = function () {
        return JSON.parse(this.decrypt());
    };

    // 服务器轮换处理
    const originalFetch = window.fetch;
    window.fetch = async function (input, init) {
        const response = await originalFetch(input, init);
        const newKey = response.headers.get("x-exchange-key");
        if (newKey) {
            SMHelper.sk = SMHelper.sm2Decrypt(newKey, SMHelper.pk);
        }
        return response;
    };

    // 客户端轮换处理
    function startRotationTimer(api) {
        const delay = Math.floor(Math.random() * 90001) + 30000; // 30s ~ 120s
        setTimeout(() => {
            getText(api).then(enc => {
                SMHelper.sk = SMHelper.sm2Decrypt(enc, SMHelper.pk);
                startRotationTimer(api);
            });
        }, delay);
    }

    return Secure;
}));

class Disguise {
    constructor(arrayBuffer) {
        this.arrayBuffer = arrayBuffer;
    }

    /**
     * 从图片中提取隐藏数据
     */
    async getKeyByImg() {
        try {
            const blob = new Blob([this.arrayBuffer], {type: "image/png"});
            const img = await loadImage(blob);
            // 创建canvas来处理图片
            const canvas = document.createElement("canvas");
            const ctx = canvas.getContext("2d");
            canvas.width = img.width;
            canvas.height = img.height;
            // 绘制图片到canvas
            ctx.drawImage(img, 0, 0);
            // 获取图片数据
            const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
            const data = imageData.data;
            // 提取二进制数据
            let binaryData = "";
            const maxLength = Math.min(data.length / 4 * 3, 100000); // 限制最大长度防止性能问题
            // 从每个像素的RGB通道提取LSB
            for (let i = 0; i < data.length && binaryData.length < maxLength; i += 4) {
                // 提取红色通道LSB
                binaryData += (data[i] & 1);
                if (binaryData.length >= maxLength) break;
                // 提取绿色通道LSB
                binaryData += (data[i + 1] & 1);
                if (binaryData.length >= maxLength) break;
                // 提取蓝色通道LSB
                binaryData += (data[i + 2] & 1);
            }
            if (binaryData.length < 32) {
                throw new Error("数据不足，无法提取长度信息");
            }
            // 提取数据长度（前32位）
            const lengthBinary = binaryData.substring(0, 32);
            const dataLength = parseInt(lengthBinary, 2);
            if (isNaN(dataLength) || dataLength < 0) {
                throw new Error("无效的数据长度");
            }
            // 检查是否有足够的数据
            if (binaryData.length < 32 + dataLength * 8) {
                throw new Error("数据不完整");
            }
            // 提取实际数据
            const dataBinary = binaryData.substring(32, 32 + dataLength * 8);
            // 将二进制转换为字符串
            return binaryToString(dataBinary);
        } catch (error) {
            if (error instanceof Error) {
                throw error;
            } else {
                throw new Error("解密过程中发生未知错误: " + error.toString());
            }
        }

        /**
         * 加载图片
         */
        function loadImage(blob) {
            return new Promise((resolve) => {
                if ("createImageBitmap" in window) {
                    createImageBitmap(blob).then(bitmap => {
                        resolve(bitmap);
                    });
                } else {
                    const img = new Image();
                    img.onload = () => resolve(img);
                    img.src = URL.createObjectURL(blob);
                }
            });
        }

        /**
         * 将二进制字符串转换为字符串
         */
        function binaryToString(binary) {
            if (binary.length % 8 !== 0) {
                throw new Error("二进制数据长度不正确");
            }
            let result = "";
            for (let i = 0; i < binary.length; i += 8) {
                const byte = binary.substr(i, 8);
                const charCode = parseInt(byte, 2);
                if (isNaN(charCode)) {
                    throw new Error("无效的二进制数据");
                }
                result += String.fromCharCode(charCode);
            }
            return result;
        }
    }

    /**
     * 从字体中提取隐藏信息
     */
    getKeyByFont() {
        try {
            const fontData = new Uint8Array(this.arrayBuffer);
            // 解析字体表结构
            const fontTables = parseFontTables(fontData);
            // 在glyf表中查找隐藏数据
            const glyfTable = fontTables.tables.get("glyf");
            if (!glyfTable) {
                throw new Error("无效的字体文件：缺少glyf表");
            }
            // 从字形数据区域提取信息
            const extractOffset = glyfTable.offset;
            const maxExtractLength = Math.min(glyfTable.length, 8192);
            const bits = new Array(maxExtractLength * 8);
            let bitCount = 0;
            for (let i = 0; i < maxExtractLength && bitCount < bits.length; i++) {
                if (extractOffset + i < fontData.length) {
                    const byte = fontData[extractOffset + i];
                    bits[bitCount++] = (byte & 1) === 1;
                }
            }
            // 转换为字节数据
            const extractedBytes = bitsToBytes(bits);
            if (extractedBytes.length < 32) {
                throw new Error("未找到有效的隐藏数据");
            }
            // 解析头部信息
            const header = extractedBytes.slice(0, 32);
            const messageLength = parseFontHeader(header);
            if (messageLength <= 0 || messageLength > extractedBytes.length - 32) {
                throw new Error("隐藏数据格式错误");
            }
            // 提取消息
            const messageBytes = extractedBytes.slice(32, 32 + messageLength);
            return new TextDecoder("utf-8").decode(messageBytes);
        } catch (error) {
            if (error instanceof Error) {
                throw error;
            } else {
                throw new Error("解密过程中发生未知错误: " + error.toString());
            }
        }

        /**
         * 解析字体表结构
         */
        function parseFontTables(fontData) {
            if (fontData.length < 12) {
                throw new Error("字体文件太小");
            }
            // 读取表数量
            const numTables = (fontData[4] << 8) | fontData[5];
            const tables = new Map();
            // 解析每个表
            const tableRecordOffset = 12;
            for (let i = 0; i < numTables; i++) {
                const recordOffset = tableRecordOffset + i * 16;
                if (recordOffset + 16 > fontData.length) break;
                // 表名
                const tableName = String.fromCharCode(
                    fontData[recordOffset],
                    fontData[recordOffset + 1],
                    fontData[recordOffset + 2],
                    fontData[recordOffset + 3]
                );
                // 偏移量和长度
                const offset = (fontData[recordOffset + 8] << 24) |
                    (fontData[recordOffset + 9] << 16) |
                    (fontData[recordOffset + 10] << 8) |
                    fontData[recordOffset + 11];
                const length = (fontData[recordOffset + 12] << 24) |
                    (fontData[recordOffset + 13] << 16) |
                    (fontData[recordOffset + 14] << 8) |
                    fontData[recordOffset + 15];
                tables.set(tableName, {offset, length});
            }
            return {tables};
        }

        /**
         * 解析字体隐写头部
         */
        function parseFontHeader(header) {
            // 验证签名
            if (header[0] !== 0x54 || header[1] !== 0x46 ||
                header[2] !== 0x53 || header[3] !== 0x01) {
                return -1;
            }
            // 验证校验和
            let expectedChecksum = header[8];
            let actualChecksum = 0;
            for (let i = 0; i < 8; i++) {
                actualChecksum ^= header[i];
            }
            if (expectedChecksum !== actualChecksum) {
                return -1;
            }
            // 提取消息长度
            return (header[4] << 24) | (header[5] << 16) |
                (header[6] << 8) | header[7];
        }

        /**
         * 位数组转字节数组
         */
        function bitsToBytes(bits) {
            const byteCount = Math.ceil(bits.length / 8);
            const bytes = new Uint8Array(byteCount);
            for (let i = 0; i < bits.length && Math.floor(i / 8) < byteCount; i++) {
                if (bits[i]) {
                    bytes[Math.floor(i / 8)] |= (1 << (7 - (i % 8)));
                }
            }
            return bytes;
        }
    }

    /**
     * 从二进制文件中提取隐藏信息
     */
    getKeyByBinary() {
        try {
            const fontData = new Uint8Array(this.arrayBuffer);
            // 提取隐藏数据
            const HEADER_SIZE = 32;
            const extractedData = extractHiddenData(fontData);
            if (!extractedData || extractedData.length < HEADER_SIZE) {
                throw new Error("未找到有效隐藏信息");
            }
            // 解析头部信息
            const header = extractedData.slice(0, HEADER_SIZE);
            const messageLength = parseHeader(header);
            if (messageLength <= 0 || messageLength > extractedData.length - HEADER_SIZE) {
                throw new Error("隐藏信息格式错误");
            }
            // 提取消息内容
            const messageBytes = extractedData.slice(HEADER_SIZE, HEADER_SIZE + messageLength);
            return new TextDecoder("utf-8").decode(messageBytes);
        } catch (error) {
            if (error instanceof Error) {
                throw error;
            } else {
                throw new Error("解密过程中发生未知错误: " + error.toString());
            }
        }

        /**
         * 从二进制数据中提取隐藏数据
         */
        function extractHiddenData(fontData) {
            try {
                // 确定提取起始位置
                const startOffset = Math.min(1000, Math.floor(fontData.length / 4));
                const maxExtractLength = Math.min(fontData.length - startOffset, Math.floor(fontData.length / 2));
                // 提取位序列
                const bits = new Array(maxExtractLength * 8);
                let bitCount = 0;
                for (let i = 0; i < maxExtractLength && bitCount < bits.length; i++) {
                    const byte = fontData[startOffset + i];
                    bits[bitCount++] = (byte & 1) === 1;
                }
                // 转换为字节数组
                return bitsToBytes(bits);
            } catch (error) {
                throw new Error("提取隐藏数据失败: " + error.message);
            }
        }

        /**
         * 解析头部信息
         */
        function parseHeader(header) {
            try {
                // 验证魔数
                if (header[0] !== 0xFF || header[1] !== 0xFE ||
                    header[2] !== 0xFD || header[3] !== 0xFC) {
                    throw new Error("无效的文件格式");
                }
                // 验证校验和
                const expectedChecksum = header[8];
                const actualChecksum = calculateChecksum(header.slice(0, 8));
                if (expectedChecksum !== actualChecksum) {
                    throw new Error("数据校验失败");
                }
                // 提取消息长度
                const lengthBytes = header.slice(4, 8);
                return bytesToInt(lengthBytes);
            } catch (error) {
                throw new Error("解析头部信息失败: " + error.message);
            }
        }

        /**
         * 将位数组转换为字节数组
         */
        function bitsToBytes(bits) {
            const byteCount = Math.ceil(bits.length / 8);
            const bytes = new Uint8Array(byteCount);
            for (let i = 0; i < bits.length && Math.floor(i / 8) < byteCount; i++) {
                if (bits[i]) {
                    bytes[Math.floor(i / 8)] |= (1 << (7 - (i % 8)));
                }
            }
            return bytes;
        }

        /**
         * 字节数组转整数
         */
        function bytesToInt(bytes) {
            return ((bytes[0] & 0xFF) << 24) |
                ((bytes[1] & 0xFF) << 16) |
                ((bytes[2] & 0xFF) << 8) |
                (bytes[3] & 0xFF);
        }

        /**
         * 计算校验和
         */
        function calculateChecksum(data) {
            let checksum = 0;
            for (let i = 0; i < data.length; i++) {
                checksum ^= data[i];
            }
            return checksum & 0xFF;
        }
    }

}